/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.ofbiz.epos.birt;

import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import javax.xml.parsers.ParserConfigurationException;

import javolution.util.FastList;
import javolution.util.FastMap;

import org.apache.axis2.classloader.IoUtil;
import org.apache.commons.io.IOUtils;
import org.apache.fop.apps.FOPException;
import org.apache.pdfbox.pdmodel.PDPage;
import org.eclipse.birt.report.engine.api.EngineException;
import org.eclipse.birt.report.engine.api.IReportEngine;
import org.eclipse.birt.report.engine.api.IReportRunnable;

import org.ofbiz.base.location.FlexibleLocation;
import org.ofbiz.base.util.Debug;
import org.ofbiz.base.util.FileUtil;
import org.ofbiz.base.util.GeneralException;
import org.ofbiz.base.util.UtilGenerics;
import org.ofbiz.base.util.UtilMisc;
import org.ofbiz.base.util.UtilValidate;
import org.ofbiz.base.util.collections.MapStack;
import org.ofbiz.base.util.string.FlexibleStringExpander;
import org.ofbiz.birt.BirtFactory;
import org.ofbiz.birt.BirtWorker;
import org.ofbiz.entity.Delegator;
import org.ofbiz.entity.GenericValue;
import org.ofbiz.entity.transaction.GenericTransactionException;
import org.ofbiz.entity.transaction.TransactionUtil;
import org.ofbiz.security.Security;
import org.ofbiz.service.DispatchContext;
import org.ofbiz.service.LocalDispatcher;
import org.ofbiz.service.ServiceUtil;
import org.ofbiz.widget.screen.ScreenRenderer;
import org.xml.sax.SAXException;

import com.lowagie.text.Document;
import com.lowagie.text.DocumentException;
import com.lowagie.text.pdf.PdfContentByte;
import com.lowagie.text.pdf.PdfReader;
import com.lowagie.text.pdf.PdfStamper;
import com.lowagie.text.pdf.PdfTemplate;
import com.lowagie.text.pdf.PdfWriter;

import java.awt.GraphicsEnvironment;
import java.awt.Rectangle;

import java.io.RandomAccessFile;


/**
 * Birt Services
 * 
 */
public class BirtServices {

    public final static String module = BirtServices.class.getName();

    
    public static int ImageResolutionFactor = 4;

    /*
     * convert Birt to PDF file
     */
    public static Map<String, Object> convertBirtToFile(DispatchContext ctx,
            Map<String, ? extends Object> context) {
        Map<String, Object> serviceContext = UtilMisc.makeMapWritable(context);
        Delegator delegator = ctx.getDelegator();
        LocalDispatcher dispatcher = ctx.getDispatcher();
        Security security = ctx.getSecurity();
        
        String birtReportLocation = (String) serviceContext
                .remove("birtReportLocation");
        Locale locale = (Locale) serviceContext.get("locale");
        Locale birtLocale = (Locale) serviceContext
                .remove(BirtWorker.BIRT_LOCALE);
        Map<String, Object> birtParameters = UtilGenerics.cast(serviceContext
                .remove(BirtWorker.BIRT_PARAMETERS));
        String birtImageDirectory = (String) serviceContext
                .remove(BirtWorker.BIRT_IMAGE_DIRECTORY);
        String birtContentType = (String) serviceContext
                .remove(BirtWorker.BIRT_CONTENT_TYPE);
        String contentType = (String) serviceContext.remove("contentType");
        String dataResourceTypeId = (String) serviceContext
                .remove("dataResourceTypeId");
        String dataTemplateTypeId = (String) serviceContext
                .remove("dataTemplateTypeId");
        String statusId = (String) serviceContext.remove("statusId");
        String mimeTypeId = (String) serviceContext.remove("mimeTypeId");
        String filePath = (String) serviceContext.remove("filePath");
        String contentTypeId = (String) serviceContext.remove("contentTypeId");
        String ownerContentId = (String) serviceContext.remove("ownerContentId");
        String stationaryLocation = (String) serviceContext.remove("stationaryLocation");

        GenericValue userLogin = (GenericValue) context.get("userLogin");
        String contentId = null;

        // check if attachment screen location passed in
        if (UtilValidate.isNotEmpty(birtReportLocation)) {
            // start processing fo pdf file
            try {
                // create the output stream for the generation
                ByteArrayOutputStream baos = new ByteArrayOutputStream();

                Map<String, Object> birtContext = FastMap.newInstance();
                if (birtLocale == null) {
                    birtLocale = locale;
                }
                birtContext.put(BirtWorker.BIRT_LOCALE, birtLocale);
                if (birtParameters != null) {
                    birtContext.put(BirtWorker.BIRT_PARAMETERS, birtParameters);
                }
                if (birtImageDirectory != null) {
                    birtContext.put(BirtWorker.BIRT_IMAGE_DIRECTORY,
                            birtImageDirectory);
                }
                if (birtContentType == null) {
                    birtContentType = "application/pdf";
                }
                IReportEngine engine = BirtFactory.getReportEngine();
                HashMap<String, Object> appContext = UtilGenerics.cast(engine
                        .getConfig().getAppContext());
                appContext.put("delegator", delegator);
                appContext.put("dispatcher", dispatcher);
                appContext.put("security", security);

                InputStream reportInputStream = BirtFactory
                        .getReportInputStreamFromLocation(birtReportLocation);
                IReportRunnable design = engine
                        .openReportDesign(reportInputStream);
                Debug.logInfo("Export report as content type:"
                        + birtContentType, module);
                BirtWorker.exportReport(design, context, birtContentType, baos);
                baos.flush();
                baos.close();

                // Write file in the file system
                File file = new File(filePath);
                File parentDir = new File(file.getParent());
                if (!parentDir.exists()) {
                    parentDir.mkdirs();
                }
                FileOutputStream foStream = new FileOutputStream(file);
                
                //Merge pdf stationary to message core
                ByteArrayOutputStream output = new ByteArrayOutputStream();
                if(UtilValidate.isNotEmpty(stationaryLocation)) {
                    stationaryLocation = FlexibleLocation.resolveLocation(stationaryLocation).toString();
                    
                    PdfReader readerBody = new PdfReader(baos.toByteArray());
                    PdfReader readerStationary = new PdfReader(stationaryLocation);
                    
                    com.lowagie.text.Rectangle psize = readerBody.getPageSize(1);
                    com.lowagie.text.Document document = new com.lowagie.text.Document(psize);
                    
                    PdfWriter writer = PdfWriter.getInstance(document, output);
                    writer.setCompressionLevel(9);
                    
                    PdfTemplate body = writer.getImportedPage(readerBody, 1 );
                    PdfTemplate stationary = writer.getImportedPage(readerStationary, 1 );
                    document.open();
                    PdfContentByte content = writer.getDirectContent();
                    content.addTemplate(body, 0, 0);
                    
                    /*
                     * transformation matrix: with addTemplate(PdfTemplate, a, b, c, d, e, f)
                     *  a  b  0
                     *  c  d  0
                     *  e  f  1 
                     */
                    content.addTemplate(stationary ,1.01f , 0, 0, 1.01f, -21.5f, -21.5f);
                    document.close();
                    baos = output;
                } 
                
                baos.writeTo(foStream);
                String fileName = file.getName();
                // Create DataResource
                Map<String, Object> dataResourceContext = FastMap.newInstance();
                dataResourceContext.put("dataResourceTypeId",
                        dataResourceTypeId);
                dataResourceContext.put("dataTemplateTypeId",
                        dataTemplateTypeId);
                dataResourceContext.put("statusId", statusId);
                dataResourceContext.put("dataResourceName", fileName);
                dataResourceContext.put("mimeTypeId", mimeTypeId);
                dataResourceContext.put("objectInfo", filePath);
                dataResourceContext.put("userLogin", userLogin);
                dataResourceContext.put("skipPermissionCheck",
                        context.get("skipPermissionCheck"));
                Map<String, Object> thisDataResourceResult = dispatcher
                        .runSync("createDataResource", dataResourceContext);
                String dataResourceId = (String) thisDataResourceResult
                        .get("dataResourceId");

                // Create Content
                Map<String, Object> contentContext = FastMap.newInstance();
                contentContext.put("contentTypeId", contentTypeId);
                contentContext.put("dataResourceId", dataResourceId);
                contentContext.put("statusId", statusId);
                contentContext.put("contentName", fileName);
                contentContext.put("mimeTypeId", mimeTypeId);
                contentContext.put("userLogin", userLogin);
                contentContext.put("ownerContentId", ownerContentId);
                contentContext.put("skipPermissionCheck",
                        context.get("skipPermissionCheck"));
                Map<String, Object> thisResult = dispatcher.runSync(
                        "createContent", contentContext);

                contentId = (String) thisResult.get("contentId");

            } catch (GeneralException ge) {
                String errMsg = "Error rendering " + birtContentType
                        + " when converting birt file: " + ge.toString();
                Debug.logError(ge, errMsg, module);
                return ServiceUtil.returnError(errMsg);
            } catch (IOException ie) {
                String errMsg = "Error I/O rendering " + birtContentType
                        + " when converting birt file: " + ie.toString();
                Debug.logError(ie, errMsg, module);
                return ServiceUtil.returnError(errMsg);
            } catch (FOPException fe) {
                String errMsg = "Error FOP rendering " + birtContentType
                        + " when converting birt file: " + fe.toString();
                Debug.logError(fe, errMsg, module);
                return ServiceUtil.returnError(errMsg);
            } catch (SAXException se) {
                String errMsg = "Error SAX rendering " + birtContentType
                        + " when converting birt file: " + se.toString();
                Debug.logError(se, errMsg, module);
                return ServiceUtil.returnError(errMsg);
            } catch (ParserConfigurationException pe) {
                String errMsg = "Error parser rendering " + birtContentType
                        + " when converting birt file: " + pe.toString();
                Debug.logError(pe, errMsg, module);
                return ServiceUtil.returnError(errMsg);
            } catch (EngineException ee) {
                String errMsg = "Error rendering " + birtContentType
                        + " when converting birt file: " + ee.toString();
                Debug.logError(ee, errMsg, module);
                return ServiceUtil.returnError(errMsg);
            } catch (SQLException se) {
                String errMsg = "Error SQL rendering " + birtContentType
                        + " when converting birt file: " + se.toString();
                Debug.logError(se, errMsg, module);
                return ServiceUtil.returnError(errMsg);
            } catch (DocumentException e) {
                Debug.logError(e, module);
                return ServiceUtil.returnError(e.getMessage());
            }
        }
        Map<String, Object> result = FastMap.newInstance();
        result.put("contentId", contentId);
        return result;
    }
}
